# 素材榜视频下载模块

import ssl_fix  # noqa: F401

import os
import json
import urllib.request
import ssl

from PyQt5.QtCore import QThread, pyqtSignal

from db_connection import DatabaseConnection


class MaterialVideoDownloader(QThread):
    """素材榜视频下载器

    从 ks_material_rank 读取数据，按配置路径下载 videoUrl 对应的视频文件。
    deleted 字段语义：0=待下载，1=已完成，2=异常。
    """

    progress_updated = pyqtSignal(str)
    download_finished = pyqtSignal()
    error_occurred = pyqtSignal(str)

    def __init__(self, db_config, download_path: str, thread_count: int = 3, filters: dict = None):
        super().__init__()
        self.db_config = db_config
        self.download_path = download_path
        self.thread_count = thread_count
        self.running = False
        self.total_downloaded_videos = 0
        # 筛选条件
        self.filters = filters or {}

    def stop(self):
        self.running = False

    # ------------------------- 主线程逻辑 -------------------------
    def run(self):
        try:
            self.running = True
            self.progress_updated.emit("开始连接数据库(素材榜)...")
            conn = DatabaseConnection.create_connection(self.db_config)
            try:
                while self.running:
                    # 一次取若干条待下载素材
                    from pymysql.cursors import DictCursor
                    with conn.cursor(DictCursor) as cursor:
                        # 构建筛选SQL
                        where_clauses = ["deleted = 0"]
                        params = []
                        
                        # 按行业筛选
                        industry = self.filters.get("industry")
                        if industry:
                            where_clauses.append("mainIndustry = %s")
                            params.append(industry)
                        
                        # 按体验分筛选
                        min_ue_score = self.filters.get("min_ue_score")
                        if min_ue_score is not None:
                            where_clauses.append("ueScore >= %s")
                            params.append(min_ue_score)
                        
                        # 按创建时间筛选(最近N天)
                        recent_days = self.filters.get("recent_days")
                        if recent_days:
                            where_clauses.append("createDate >= DATE_SUB(NOW(), INTERVAL %s DAY)")
                            params.append(recent_days)
                        
                        # 按视频长度筛选
                        min_duration = self.filters.get("min_duration")
                        max_duration = self.filters.get("max_duration")
                        if min_duration is not None:
                            where_clauses.append("duration >= %s")
                            params.append(min_duration)
                        if max_duration is not None:
                            where_clauses.append("duration <= %s")
                            params.append(max_duration)
                        
                        where_sql = " AND ".join(where_clauses)
                        select_sql = f"SELECT * FROM ks_material_rank WHERE {where_sql} LIMIT %s"
                        params.append(self.thread_count)
                        
                        cursor.execute(select_sql, tuple(params))
                        records = cursor.fetchall()

                    if not records:
                        self.progress_updated.emit("没有更多素材需要下载")
                        break

                    self.progress_updated.emit(f"获取到 {len(records)} 条素材进行处理")

                    for record in records:
                        if not self.running:
                            break
                        try:
                            # 标记为处理中
                            with conn.cursor() as u:
                                u.execute("UPDATE ks_material_rank SET deleted = 2 WHERE id = %s", (record["id"],))
                                conn.commit()

                            video_url = record.get("videoUrl") or ""
                            if not video_url:
                                # 尝试从 materialJson 中解析
                                try:
                                    raw = record.get("materialJson")
                                    if raw:
                                        data = json.loads(raw)
                                        video_url = data.get("videoUrl") or ""
                                except Exception:
                                    pass

                            if not video_url:
                                self.progress_updated.emit(f"警告: 素材 {record.get('materialId')} 未找到视频地址，跳过")
                                with conn.cursor() as u:
                                    u.execute("UPDATE ks_material_rank SET deleted = 1 WHERE id = %s", (record["id"],))
                                    conn.commit()
                                continue

                            # 获取商品信息用于目录和文件命名
                            item_id = record.get("itemId") or "unknown"
                            title = record.get("title") or "无标题"
                            safe_title = self.clean_filename(title)
                            
                            # 根据分类字段选项创建目录结构
                            classify_by = self.filters.get("classify_by", "categoryLevel1Name")  # 默认一级类目
                            
                            if classify_by == "none":
                                # 不分类：直接放在素材榜视频根目录下
                                base_dir = os.path.join(self.download_path, "素材榜视频")
                            else:
                                # 按分类字段值分类，不再创建商品ID子文件夹
                                category_value = record.get(classify_by) or "未分类"
                                safe_category = self.safe_folder_name(category_value)
                                # 创建目录：素材榜视频/{分类字段值}
                                base_dir = os.path.join(self.download_path, "素材榜视频", safe_category)
                            
                            os.makedirs(base_dir, exist_ok=True)

                            # 文件命名格式：商品id{itemId}视频标题{title}01.mp4（与视频下载tab保持一致）
                            filename = f"商品id{item_id}视频标题{safe_title}01.mp4"
                            file_path = os.path.join(base_dir, filename)

                            self._download_video(video_url, file_path, str(item_id))

                            with conn.cursor() as u:
                                u.execute("UPDATE ks_material_rank SET deleted = 1 WHERE id = %s", (record["id"],))
                                conn.commit()

                        except Exception as e:
                            with conn.cursor() as u:
                                u.execute("UPDATE ks_material_rank SET deleted = 2 WHERE id = %s", (record["id"],))
                                conn.commit()
                            self.error_occurred.emit(f"处理素材 {record.get('materialId')} 时出错: {e}")
                            continue

                self.progress_updated.emit(f"素材榜视频下载任务完成! 累计下载了 {self.total_downloaded_videos} 个视频")
                self.download_finished.emit()
            finally:
                conn.close()
        except Exception as e:
            self.error_occurred.emit(f"素材榜视频下载过程中出错: {e}")
        finally:
            self.running = False

    # ------------------------- 实际下载 -------------------------
    def _download_video(self, url: str, file_path: str, material_id: str):
        # 创建不验证SSL证书的上下文（与主程序保持一致）
        context = ssl.create_default_context()
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE

        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
        }
        req = urllib.request.Request(url, headers=headers)

        max_retries = 3
        for retry in range(max_retries):
            try:
                with urllib.request.urlopen(req, context=context, timeout=60) as resp:
                    chunk_size = 8192
                    with open(file_path, "wb") as f:
                        while True:
                            chunk = resp.read(chunk_size)
                            if not chunk:
                                break
                            f.write(chunk)
                self.total_downloaded_videos += 1
                self.progress_updated.emit(
                    f"下载素材视频: 素材ID{material_id} -> {os.path.basename(file_path)} (累计下载: {self.total_downloaded_videos} 个视频)"
                )
                return
            except urllib.error.HTTPError as http_err:
                if http_err.code == 404:
                    self.progress_updated.emit(f"警告: 素材 {material_id} 的视频文件不存在 (404 Not Found): {url}")
                    return
                if retry < max_retries - 1:
                    self.progress_updated.emit(f"下载素材 {material_id} 第{retry+1}次失败，准备重试: HTTP {http_err.code} - {http_err.reason}")
                else:
                    raise
            except Exception as e:
                if retry < max_retries - 1:
                    self.progress_updated.emit(f"下载素材 {material_id} 第{retry+1}次失败，准备重试: {e}")
                else:
                    raise

    # ------------------------- 辅助函数 -------------------------
    def clean_filename(self, filename: str) -> str:
        illegal_chars = '<>:"/\\|?*'
        for ch in illegal_chars:
            filename = filename.replace(ch, " ")
        filename = filename.strip()
        if len(filename) > 80:
            filename = filename[:80]
        return filename or "未命名"

    def safe_folder_name(self, name: str) -> str:
        if not name:
            return "未知作者"
        return self.clean_filename(str(name)) or "未知作者"
